/**
 * 
 */
package co.edu.javeriana.ia.ga;

import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.math3.random.RandomData;
import org.apache.commons.math3.random.RandomDataImpl;
import org.jgap.BaseGene;
import org.jgap.Configuration;
import org.jgap.Gene;
import org.jgap.InvalidConfigurationException;
import org.jgap.RandomGenerator;
import org.jgap.UnsupportedRepresentationException;

/**
 * <p>
 * Gen que representa una regla, las reglas son evaluadas individualmente y su
 * cumplimiento agrega "valor a la funcion de fitness"
 * </p>
 * 
 * @author <a href=f.triana@javeriana.edu.co">Alejandro Triana</a>
 * @version 1.0
 */
public class RuleGene extends BaseGene implements Gene {

	private static final long serialVersionUID = 1L;

	private RuleAllele ruleAllele;

	public RuleGene(Configuration conf, RuleAllele ruleAllele)
			throws InvalidConfigurationException {
		super(conf);
		this.ruleAllele = ruleAllele;
		setAllele(ruleAllele);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
	 */
	@Override
	public int compareTo(Object o) {

		if (o == null) {
			return 1;
		}

		// Se realiza un ordenamiento por el valor de la condicion
		if (o instanceof RuleGene) {

			RuleGene oGene = (RuleGene) o;

			if (this.ruleAllele.equals(oGene.getAllele())) {
				return 0;
			} else {
				// Siempre se retorna que le gen actual es menor que el
				// comparado
				// Esto se realiza para realizar la seleccion natural
				return -1;
			}

		} else {
			throw new ClassCastException();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jgap.BaseGene#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object o) {
		return compareTo(o) == 0;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jgap.Gene#applyMutation(int, double)
	 */
	@Override
	public void applyMutation(int aIndex, double aPercentage) {

		RandomData rd = new RandomDataImpl();
		Collection<Descriptor> descriptors = ruleAllele.getCondition().values();

		int descriptorToMutation = rd.nextInt(0, descriptors.size() - 1);

		int index = 0;
		for (Descriptor descriptorItem : descriptors) {
			
			if (index == descriptorToMutation && !descriptorItem.isMatch()) {
				
				double absPercentage = Math.abs(aPercentage);
				int descriptorPercentage = (int) (descriptorItem.getFrecuency() * absPercentage);
				
				descriptorItem.setFrecuency(descriptorPercentage);
				break;
			}
			index++;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jgap.Gene#getPersistentRepresentation()
	 */
	@Override
	public String getPersistentRepresentation()
			throws UnsupportedOperationException {

		StringBuilder strBuilder = new StringBuilder();

		strBuilder.append(ruleAllele.getCondition()).append(
				Constants.CONDITION_SEPARATOR);
		strBuilder.append(ruleAllele.getAction()).append(
				Constants.RULE_SEPARATOR);

		return strBuilder.toString();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jgap.Gene#setValueFromPersistentRepresentation(java.lang.String)
	 */
	@Override
	public void setValueFromPersistentRepresentation(String str)
			throws UnsupportedOperationException,
			UnsupportedRepresentationException {

		String[] rule = str.split(Constants.RULE_SEPARATOR);

		// this.ruleAllele.setCondition(Descriptor.getFromString(rule[0]));
		this.ruleAllele.setAction(Genome.getFromString(rule[1]));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jgap.Gene#setAllele(java.lang.Object)
	 */
	@Override
	public void setAllele(Object allele) {
		this.ruleAllele = (RuleAllele) allele;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jgap.Gene#setToRandomValue(org.jgap.RandomGenerator)
	 */
	@Override
	public void setToRandomValue(RandomGenerator rnd) {
		RandomData ran = new RandomDataImpl();

		// Se establece aleatoriamente el valor de la frecuencia
		Map<String, Descriptor> conditionDes = this.ruleAllele.getCondition();
		Set<Entry<String, Descriptor>> conditionDesMap = conditionDes
				.entrySet();

		for (Entry<String, Descriptor> entryDes : conditionDesMap) {
			entryDes.getValue().setFrecuency(
					ran.nextInt(Constants.MIN_VALUE_FRECUENCY_DESCRIPTOR,
							Constants.MAX_VALUE_FRECUENCY_DESCRIPTOR));
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jgap.BaseGene#getInternalValue()
	 */
	@Override
	protected Object getInternalValue() {
		return this.ruleAllele;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jgap.BaseGene#newGeneInternal()
	 */
	@Override
	protected Gene newGeneInternal() {
		try {
			return new RuleGene(getConfiguration(), this.ruleAllele);
		} catch (InvalidConfigurationException e) {
			return null;
		}
	}
}
